﻿<#
 .DESCRIPTION
  This module contains cmdlet to help writing Powershell scripts against Team
  Foundation Server.
  
 .NOTES
  Copyright © Microsoft Corporation. All Rights Reserved.
  This code released under the terms of the 
  Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
  This is sample code only, do not use in production environments
#>

# turns on interpreters checks
Set-StrictMode –version Latest

# ----------------------------------- BASICS -----------------------------------

<#
 .SYNOPSIS
 Get the client TFS object representing a TFS Server Configuration.
 
 .DESCRIPTION
 Get the client Microsoft.TeamFoundation.Client .TfsConfigurationServer object representing a TFS Server Configuration.
 
 It has properties that represent the more commonly used services:
	TPCS 	ITeamProjectCollectionService
	TFJS	ITeamFoundationJobService
 
 Please see http://msdn.microsoft.com/EN-US/library/bb286958.aspx#services for list of available services
 
 .PARAMETER TfsServerUrl
  A single name of a TFS server to connect to.
 
 .EXAMPLE
  (Get-Tfs "http://MyTFSServer:8080/tfs").TPCS.GetCollections()

  Lists all team project collections on the server.
#>
function Get-Tfs
{
[CmdletBinding()]
param (
    [parameter(ParameterSetName="TfsServerUrl", Mandatory=$true, Position=0)]
    [alias("Tfs")]
    [alias("TfsServer")]
    [alias("Server")]
    [alias("Url")]
    [string] $TfsServerUrl = $(throw 'TfsServerUrl is required')
    )
begin
{
    # load the required dll version independent
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")

    $propertiesToAdd = (
		('TPCS', 'Microsoft.TeamFoundation.Client', 'Microsoft.TeamFoundation.Framework.Client.ITeamProjectCollectionService'),
		('TFJS', 'Microsoft.TeamFoundation.Client', 'Microsoft.TeamFoundation.Framework.Client.ITeamFoundationJobService')
    )
}

process
{
	if (![System.Uri]::IsWellFormedUriString($TfsServerUrl, 'Absolute')) 
	{
		# server name only?
		$ub = New-Object UriBuilder "http", $TfsServerUrl, 8080, "tfs"
		$TfsServerUrl = $ub.Uri.AbsoluteUri
	}
    # fetch the TFS instance, but add some useful properties to make life easier
    # Make sure to "promote" it to a psobject now to make later modification easier
	$tfsServerUri = New-Object Uri $TfsServerUrl
    [psobject] $tfs = [Microsoft.TeamFoundation.Client.TfsConfigurationServerFactory]::GetConfigurationServer($tfsServerUri)
	
	if ($tfs -eq $null)
	{
		Write-Error "Unable to connect to the server " $tfsServerUri
	}
	
    foreach ($entry in $propertiesToAdd)
    {
        $scriptBlock = '
            [System.Reflection.Assembly]::LoadWithPartialName("{0}") > $null
            $this.GetService([{1}])
        ' -f $entry[1],$entry[2]
        $tfs | add-member scriptproperty $entry[0] $ExecutionContext.InvokeCommand.NewScriptBlock($scriptBlock)
    }
	
    return $tfs
}

}#cmdlet

<#
 .SYNOPSIS
  Get the client TFS object representing a TFS TeamProjectCollection.
 
 .DESCRIPTION
 Get the client Team Foundation Server object representing a TFS TeamProjectCollection.
 It has properties that represent the more commonly used services:
    VCS			VersionControlServer
    WIT			WorkItemStore
    CSS			ICommonStructureService
    GSS			IGroupSecurityService
	BuildServer	IBuildServer
 
 Please see http://msdn.microsoft.com/EN-US/library/bb286958.aspx#services for list of available services
 
 .PARAMETER Server
  Microsoft.TeamFoundation.Client.TfsConfigurationServer object which may be returned by executing Get-Tfs command.
 
 .PARAMETER Name
  Optional parameter which indicates name of the project collection to be returned.
  May contain wildcard and "*" value wiil be used if parameter is ommited.
 
 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection -Name "*Ranger*"

  Return all project collections that contains word "Ranger" in the name.

 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection

  Return all project collections from the server.
#>
function Get-TfsCollection
{
	#TODO: Update description
	[CmdletBinding()]
	param (
	    [parameter(ParameterSetName="Server", Mandatory=$true, Position=1, ValueFromPipeline=$true)]
		[psobject] $Server,
		
		# Don't need TPCS because it requires "Edit instance-level information" permission for the server
		#[parameter(ParameterSetName="Server", Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)]
		#[Microsoft.TeamFoundation.Framework.Client.ITeamProjectCollectionService] $TPCS,
	
		[string] $Name = "*"
	    )
	begin
	{
	    # load the required dll version independent
	    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")

	    $propertiesToAdd = (
	        ('VCS', 'Microsoft.TeamFoundation.VersionControl.Client', 'Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer'),
	        ('WIT', 'Microsoft.TeamFoundation.WorkItemTracking.Client', 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore'),
	        ('CSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.ICommonStructureService'),
	        ('GSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.IGroupSecurityService'),
			('BuildServer', 'Microsoft.TeamFoundation.Build.Client', 'Microsoft.TeamFoundation.Build.Client.IBuildServer')
	    )
		
		# ugly HACK, but PS is not able to match an Array for an IEnumerable<T>
		$resourceTypeFilters = New-Object "System.Collections.Generic.List``1[[System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
		$resourceTypeFilters.Add([Microsoft.TeamFoundation.Framework.Common.CatalogResourceTypes]::ProjectCollection)
		$resourceCatalogOptions = [Microsoft.TeamFoundation.Framework.Common.CatalogQueryOptions]::None
	}

	process
	{
		$catalogNode = $Server.CatalogNode
		if ($catalogNode -eq $null)
		{
			Write-Error -Message "Unable to connect to the server $Server" -ErrorAction Stop
		}
		$nodes = $catalogNode.QueryChildren($resourceTypeFilters, $false, $resourceCatalogOptions) | where { $_.Resource.DisplayName -like $Name }
		
		if ($nodes -ne $null)
		{
			foreach ($node in $nodes)
			{ 
				# fetch the TFS instance, but add some useful properties to make life easier
			    # Make sure to "promote" it to a psobject now to make later modification easier
				[psobject] $tfsCollection = $Server.GetTeamProjectCollection($node.Resource.Properties["InstanceId"]) 
			    foreach ($entry in $propertiesToAdd) {
			        $scriptBlock = '
			            [System.Reflection.Assembly]::LoadWithPartialName("{0}") > $null
			            $this.GetService([{1}])
			        ' -f $entry[1],$entry[2]
			        $tfsCollection | add-member scriptproperty $entry[0] $ExecutionContext.InvokeCommand.NewScriptBlock($scriptBlock)
			    }
				
				$tfsCollection
			}
		}
	}
}

<#
 .SYNOPSIS
  Get team projects the client TFS object representing a TFS TeamProjectCollection.
 
 .DESCRIPTION
 Get the object representing a TFS Team Project.

 
 .PARAMETER ProjectCollection
  Object that represents project collection. Usually it returned by executing Get-TfsCollection command.
 
 .PARAMETER Name
  Optional parameter which indicates name of the project to be returned.
  May contain wildcard and "*" value wiil be used if parameter is ommited.
 
 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject -Name "*Guidance*"

  Return all projects that contains word "Guidance" in the name.

 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject

  Return all projects from the collection.
#>
function Get-TfsProject
{
	#TODO: Update description
	[CmdletBinding()]
	param (
	    [parameter(ParameterSetName="ProjectCollection", Mandatory=$true, Position=1, ValueFromPipeline=$true)]
		[psobject] $ProjectCollection,
		
		[string] $Name = "*"
	    )
	process
	{
		$ProjectCollection.CSS.ListAllProjects() | where { $_.Name -like $Name } | Add-Member -MemberType NoteProperty -Name "Collection" -Value $ProjectCollection -PassThru
	}
}

<#
 .SYNOPSIS
  Get work item type definition TFS object representing a TFS Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType 
 
 .DESCRIPTION
 Get the object representing a TFS work item type definition.

 
 .PARAMETER Project
  Object that represents team project. Usually it returned by executing Get-TfsProject command.
 
 .PARAMETER Name
  Optional parameter which indicates name of the project to be returned.
  May contain wildcard and "*" value wiil be used if parameter is ommited.
 
 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject | Get-TfsWorkItemType -Name "Bug*"

  Return all work item type definitions that contains word "Bug" in the name.

 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject | Get-TfsWorkItemType

  Return all work item type definitions from the team project.
#>
function Get-TfsWorkItemType
{
	#TODO: Update description
	[CmdletBinding()]
	param (
	    [parameter(ParameterSetName="Project", Mandatory=$true, Position=1, ValueFromPipeline=$true)]
		[psobject] $Project,
		
		[string] $Name = "*"
	    )
	process
	{
		$Project.Collection.WIT.Projects[$Project.Name].WorkItemTypes | where { $_.Name -like $Name }
	}
}

<#
 .SYNOPSIS
  Export work item type definition as XML
 
 .DESCRIPTION
  Export work item type definition as XML string.

 
 .PARAMETER WorkItemType
  Object that represents work item type definition. Usually it returned by executing Get-TfsWorkItemType command.
 
 .PARAMETER IncludeGlobalLists
  Export global lists among with work item type definiton.
   
 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject -Name "My source project" | Get-TfsWorkItemType -Name "Bug" | Export-TfsWorkItemTypeXml

  Return all work item type definitions that contains word "Bug" in the name as XML.

 .EXAMPLE
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject | Get-TfsWorkItemType | Export-TfsWorkItemTypeXml -IncludeGlobalLists

  Return all work item type definitions from the team project as XML.
#>
function Export-TfsWorkItemTypeXml
{
	#TODO: Update description
	[CmdletBinding()]
	param (
	    [parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
		[psobject] $WorkItemType,
		[switch] $IncludeGlobalLists = $false
	    )
	process
	{
		$WorkItemType.Export($IncludeGlobalLists).InnerXml
	}
}

<#
 .SYNOPSIS
  Import work item type definition into Team Project 
 
 .DESCRIPTION
  Import work item type definition represented by XML into the Team Project.

 
 .PARAMETER Project
  Object that represents team project. Usually it returned by executing Get-TfsProject command.
 
 .PARAMETER WorkItemTypeDefinition
  string that contain XML representaion of the work item type definition to be imported.
   
 .EXAMPLE
  $targetProject = Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject -Name "My destination project"
  Get-Tfs "http://MyTFSServer:8080/tfs" | Get-TfsCollection | Get-TfsProject -Name "My source project" | Get-TfsWorkItemType -Name "Bug" | Export-TfsWorkItemTypeXml -IncludeGlobalLists | Import-TfsWorkItemTypeXml -Project $targetProject

  Clone Bug work item type definition.
#>
function Import-TfsWorkItemTypeXml
{
	#TODO: Update description
	[CmdletBinding()]
	param (
	    [parameter(Mandatory=$true, Position=1, ValueFromPipeline=$false)]
		[psobject] $Project,
		
	    [parameter(Mandatory=$true, Position=2, ValueFromPipeline=$true)]
		[string] $WorkItemTypeDefinition
		)
	process
	{
		try
		{		
			# verify against target, may raise an exception
			#[Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType]::Validate($Project.Collection.WIT.Projects[$Project.Name], $WorkItemTypeDefinition)
			
			# import the work item type definition xml into the destination project
			# if no exceptions, then clone was successfull.
			$Project.Collection.WIT.Projects[$Project.Name].WorkItemTypes.Import($WorkItemTypeDefinition)
		}
		catch [System.Xml.XmlException]
		{
			throw "Xml Exception: \n\n$($_.Message)"
		}
		catch [System.Xml.Schema.XmlSchemaValidationException]
		{
			throw "Xml Schema Validation Exception: \n\n$($_.Message)"
		}
	}
}

Export-ModuleMember -Function Get-Tfs
Export-ModuleMember -Function Get-TfsCollection
Export-ModuleMember -Function Get-TfsProject
Export-ModuleMember -Function Get-TfsWorkItemType
Export-ModuleMember -Function Export-TfsWorkItemTypeXml
Export-ModuleMember -Function Import-TfsWorkItemTypeXml